/**
 * Created by IntelliJ IDEA.
 * User: bilesja
 * Date: 4/25/11
 * Time: 1:30 PM
 * To change this template use File | Settings | File Templates.
 */



import com.sun.corba.se.spi.transport.CorbaAcceptor;

import java.util.*;

public class Hash {

    HashMap<Integer,BigArea> map;
    int row_height;
    int column_width;
    int total_height;
    int total_width;
    int num_locations;


    Hash(int height, int width, int num_columns, int num_rows){
        row_height = height/num_rows;
        column_width = width/num_columns;
        total_height = height;
        total_width = width;
        int left_x;
        int upper_y;
        num_locations = 0;

        map = new HashMap<Integer,BigArea>();
        for (int i = 0; i<num_columns; i++){
            upper_y =  i*column_width;
            for (int j = 0; j<num_rows; j++){
                left_x = j*row_height;
                map.put(makeHash(left_x,upper_y), new BigArea(row_height,column_width,left_x,upper_y));
            }
        }
    }

    public int makeHash(int x, int y){
        return (x*total_height+y);
    }

    public void addCoordinate(int x, int y){
        int left_x = x-(x%column_width);
        int upper_y = y-(y%row_height);
        BigArea thisArea = map.get(makeHash(left_x,upper_y));
        Coordinates newCoords = new Coordinates(x,y);
        thisArea.addPoint(newCoords);
        num_locations++;
    }

    public ArrayList<Coordinates> find_near(int num_to_find, int x, int y){
        int left_x = x-(x%column_width);
        int upper_y = y-(y%row_height);
        if (num_to_find > num_locations) num_to_find = num_locations;
        int num_found = 0;
        int bigRadius = 1;

        //finds at least the number of required items
        ArrayList<Coordinates> coordinatesFound = new ArrayList<Coordinates>();
        while (num_found < num_to_find){
            coordinatesFound = search(bigRadius,left_x, upper_y);
            num_found = coordinatesFound.size();
            bigRadius++;
        }
        //puts all the coordinates found into a TreeMap sorted based on distance from x,y
        TreeMap<Double, Coordinates> sortedCoordinates = new TreeMap<Double,Coordinates>();
        Coordinates thisCoord;
        for (int i = 0; i<coordinatesFound.size(); i++){
            thisCoord = coordinatesFound.get(i);
            sortedCoordinates.put(thisCoord.calculateDistance(x, y),thisCoord);
        }

        //takes the first n items from the list
        ArrayList<Coordinates> returnSet = new ArrayList<Coordinates>();
        for (int i = 0; i<num_to_find; i++){
            returnSet.add(sortedCoordinates.pollFirstEntry().getValue());
        }

        return returnSet;
    }

    private  ArrayList<Coordinates> search(int radius, int left_x, int upper_y){
        ArrayList<Coordinates> returningCoordinates = new ArrayList<Coordinates>();

        //finds everything in the middle row
        returningCoordinates.addAll(searchHorizontal(radius,left_x,upper_y));

        //finds everything in the upper and lower rows
        for (int i = 1; i<radius; i++){
            returningCoordinates.addAll(searchHorizontal(radius-i,left_x,upper_y+i*row_height));
            returningCoordinates.addAll(searchHorizontal(radius-i,left_x,upper_y-i*row_height));
        }

        return returningCoordinates;
    }

    private ArrayList<Coordinates> searchHorizontal(int numHorizontal, int left_x, int upper_y){
        ArrayList<Coordinates> returningCoordinates = new ArrayList<Coordinates>();

        int thisCorner = makeHash(left_x,upper_y);
        BigArea thisArea;

        //add in the center area
        if (map.containsKey(thisCorner)){
            thisArea = map.get(thisCorner);
            returningCoordinates.addAll(thisArea.getPoints());
        }

        //add in everything to the left
        for (int i = 1; i<=numHorizontal; i++){
            thisCorner = makeHash(left_x - i*column_width,upper_y);
            if (map.containsKey(thisCorner)){
                thisArea = map.get(thisCorner);
                returningCoordinates.addAll(thisArea.getPoints());
            }
        }

        //add in everything to the right
        for (int i = 1; i<=numHorizontal; i++){
            thisCorner = makeHash(left_x + i*column_width,upper_y);
            if (map.containsKey(thisCorner)){
                thisArea = map.get(thisCorner);
                returningCoordinates.addAll(thisArea.getPoints());
            }
        }

        return returningCoordinates;
    }
}
